
use std::collections::HashSet;


fn main() {
    println!("闭包学习");

    {
        // 第8章 闭包表达式
        // https://minstrel1977.gitee.io/rust-reference/expressions/closure-expr.html

        fn three_times<F>(f: F) where F: Fn(i32) {
            for i in 0..3 {
                f(i);
            }
        }        

        three_times(|i| println!("Hello, {}", i));
        
        let word = "closure".to_owned();
        // 使用移动语义从环境中捕获变量的
        three_times(move |j| println!("{}: {}", word, j));
    }

    {
        // 第10章 闭包类型
        // https://minstrel1977.gitee.io/rust-reference/types/closure.html

        // 闭包表达式生成的闭包值具有唯一性和无法写出的匿名性。闭包类型近似相当于包含捕获变量的结构体。

        fn f<F: FnOnce() -> String> (g: F) {
            println!("{}", g());
        }

        let mut s = String::from("foo");
        let t = String::from("bar");

        f (|| {
            s += &t;
            s
        });        

        /**
         * 捕获方式
         * 
         * 编译器倾向于优先通过不可变借用(immutable borrow)来捕获闭合变量(closed-over variable)，
         * 其次是通过唯一不可变借用(unique immutable borrow)（见下文），再其次可变借用(mutable borrow)，最后使用移动语义(move)来捕获。
         * 
         * 编译器将选择与捕获的变量在闭包体中的使用方式兼容的第一种捕获方式。
         * 编译器不会考虑周围的代码，比如所涉及的变量的生存期，或者闭包本身的生存期。
         * 如果使用了关键字 move ，那么所有捕获都是通过移动(move)语义进行的（当然对于 Copy类型，则是通过拷贝语义进行的），而不管借用是否可用。
         * 关键字 move 通常用于允许闭包比其捕获的值活得更久，例如返回闭包或用于生成新线程。
         * 
         * 复合类型（如结构体、元组和枚举）始终是全部捕获的，而不是各个字段分开捕获的。
         * 如果真要捕获单个字段，那可能需要先借用该字段到本地局部变量中：
         */
        #[allow(unused)]
        struct SetVec {
            set: HashSet<u32>,
            vec: Vec<u32>,
        }
        
        #[allow(unused)]
        impl SetVec {
            fn populate(&mut self) {
                let vec = &mut self.vec;
                self.set.iter().for_each(|&n| {
                    vec.push(n);
                })
            }
        }

        /*
         * 捕获中的唯一不可变借用
         * 
         * 捕获方式中有一种被称为唯一不可变借用的特殊类型的借用捕获，这种借用不能在语言的其他任何地方使用，也不能显式地写出。
         * 唯一不可变借用发生在修改可变引用的引用对象(referent)时，如下面的示例所示：
         */
        let mut b = false;
        let x = &mut b;
        {
            let mut c = || { *x = true; };
            c();
        }
    }
}